import logging
import sys
from dataclasses import dataclass
from json import loads, dumps

from school_ringer_modules.config import config_path
from school_ringer_modules.sound import default_sound
from school_ringer_modules.system import run_command


@dataclass
class GroupOfCalls:
    name: str
    start: str
    end: str
    pre: str

    def to_dict(self) -> dict:
        """
        Метод, возвращающий словарь с ключами из полей объекта
        :return: словарь, полученный из объекта класса
        """
        return {
            "name": self.name,
            "start": self.start,
            "end": self.end,
            "pre": self.pre
        }


class AllGroupsOfCalls:
    def __init__(self) -> None:
        """
        Базовый конструктор, создающий объект класса из json-файла с группами
        Если файла нет, он создаётся
        """
        self.filename = f'{config_path}/groups.json'
        try:
            with open(self.filename, 'r', encoding='utf-8') as file:
                if not file.read():
                    self.clean()
            self.groups: dict = self.read()
            logging.info(f'Из файла прочитан список групп: {self.groups}')
        except FileNotFoundError:
            self.groups = {
                "По умолчанию": {
                    "name": "По умолчанию",
                    "start": default_sound(),
                    "end": default_sound(),
                    "pre": default_sound()
                }
            }
            self.write(self.groups)
            logging.info(f'Файл с группами не найден, создана и записана в файл группа по умолчанию')

    def __getitem__(self, item: str) -> GroupOfCalls:
        """
        Возвращает объект - группу звонков GroupOfCalls - по имени группы
        :param item: название группы
        :return: группа звонков с указанным названием
        """
        return GroupOfCalls(
            name=self.groups[item]['name'],
            start=self.groups[item]['start'],
            end=self.groups[item]['end'],
            pre=self.groups[item]['pre']
        )

    def items_to_list(self) -> [GroupOfCalls]:
        """
        Возвращает список всех групп звонков
        :return: список групп звонков
        """
        result = []
        for item in self.groups:
            result.append(GroupOfCalls(
                name=self.groups[item]['name'],
                start=self.groups[item]['start'],
                end=self.groups[item]['end'],
                pre=self.groups[item]['pre']
            ))
        return result

    def read(self) -> dict:
        """
        Возвращает словарь групп звонков или завершает программу при отсутствии файла
        @return: словарь с группами звонков
        """
        try:
            with open(self.filename, 'r', encoding='utf-8') as file:
                return loads(file.read())
        except FileNotFoundError:
            logging.info('[error] Файл ' + self.filename + ' не найден\nАварийная остановка программы')
            sys.exit(0)

    def write(self, value, filename='') -> None:
        """
        Записывает группы звонков в файл. При ошибке возвращает None
        :return: None
        @param value: словарь групп звонков
        @param filename: имя файла для записи, если на задано, то стандартный файл групп в конфиге
        """
        if not filename:
            filename = self.filename
        try:
            tempfile = run_command('mktemp').strip()
            with open(tempfile, 'w', encoding='utf-8') as file:
                file.write(dumps(value, indent=4, ensure_ascii=False))
                logging.info(f'Группы звонков записаны в файл {tempfile}')
            if filename == self.filename:
                run_command(f'pkexec sh -c "mv {tempfile} {filename} && chmod 644 {filename}"')
            else:
                run_command(f'mv {tempfile} {filename} && chmod 644 {filename}')
            logging.info(f'Группы звонков перенесены в файл {filename}')
        except KeyError:
            logging.info('[error] Ключ не найден')
            return None

    def clean(self) -> None:
        """
        Очищает все группы звонков,
        оставляет только группу По умолчанию
        """
        self.groups = {
            "По умолчанию": {
                "name": "По умолчанию",
                "start": default_sound(),
                "end": default_sound(),
                "pre": default_sound()
            }
        }
        logging.info(f'Группы звонков очищены, оставлена только группа по умолчанию')

    def __setitem__(self, key: str, new_group: GroupOfCalls):
        """
        Добавляет ко всем группам звонков новую группу
        :param key: название группы
        :param new_group: группа звонков
        :return: все группы звонков вместе с добавленной
        """
        logging.info(f"Установлена новая группа: {new_group}")
        self.groups[key] = new_group.to_dict()
        # self._write(self.groups)
        return self

    def __delitem__(self, key: str):
        """
        Удаляет группу звонков по ключу
        :param key: название группы
        :return: все группы звонков вместе без удалённой
        """
        logging.info(f'Удалена группа {key}')
        del self.groups[key]
        # self._write(self.groups)
        return self

    def __repr__(self):
        """
        Выводит группы звонков
        """
        print('------Groups of calls:-------')
        for group_name in self.groups:
            group = self.groups[group_name]
            print(f'{group["name"]}: start - {group["start"]}, end - {group["end"]}, pre - {group["pre"]}')


logging.info('Созданы все группы звонков')
all_groups_of_calls = AllGroupsOfCalls()

